home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkGrid.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  57.3 KB  |  2,053 lines

  1. /* 
  2.  * tkGrid.c --
  3.  *
  4.  *    Grid based geometry manager.
  5.  *
  6.  * Copyright (c) 1996 by Sun Microsystems, Inc.
  7.  *
  8.  * See the file "license.terms" for information on usage and redistribution
  9.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  10.  *
  11.  *
  12.  * SCCS: @(#) tkGrid.c 1.21 96/02/21 10:50:58
  13.  */
  14.  
  15. #include "tkInt.h"
  16.  
  17. /*
  18.  * LayoutInfo structure.  We shouldn't be using hard-wired limits!
  19.  */
  20.  
  21. #define MAXGRIDSIZE 128
  22. #ifndef MAXINT
  23. #  define MAXINT 0x7fff
  24. #endif
  25. #define MINWEIGHT    0.0001        /* weight totals < this are considered to be zero */
  26.  
  27. /*
  28.  * Special characters to support relative layouts
  29.  */
  30.  
  31. #define REL_SKIP    'x'    /* skip this column */
  32. #define REL_HORIZ    '-'    /* extend previous widget horizontally */
  33. #define REL_VERT    '^'    /* extend previous widget verticallly */
  34.  
  35. /*
  36.  *  structure to hold collected constraints temporarily:
  37.  *  needs to use a "Constrain" thingy
  38.  */
  39.  
  40. typedef struct {
  41.     int width, height;        /* number of cells horizontally, vertically */
  42.     int lastRow;            /* last cell with a window in it */
  43.     int minWidth[MAXGRIDSIZE];    /* largest minWidth in each column */
  44.     int minHeight[MAXGRIDSIZE];    /* largest minHeight in each row */
  45.     double weightX[MAXGRIDSIZE];    /* largest weight in each column */
  46.     double weightY[MAXGRIDSIZE];    /* largest weight in each row */
  47. } LayoutInfo;
  48.  
  49. /* structure for holding row and column constraints */
  50.  
  51. typedef struct {
  52.     int used;        /* maximum element used */
  53.     int max;        /* maximum element allocated */
  54.     int *minsize;        /* array of minimum column/row sizes */
  55.     double *weight;    /* array of column/row weights */
  56. } Constrain;
  57.  
  58. /* For each window that the gridbag cares about (either because
  59.  * the window is managed by the gridbag or because the window
  60.  * has slaves that are managed by the gridbag), there is a
  61.  * structure of the following type:
  62.  */
  63.  
  64. typedef struct GridBag {
  65.     Tk_Window tkwin;        /* Tk token for window.  NULL means that
  66.                  * the window has been deleted, but the
  67.                  * packet hasn't had a chance to clean up
  68.                  * yet because the structure is still in
  69.                  * use. */
  70.     struct GridBag *masterPtr;    /* Master window within which this window
  71.                  * is managed (NULL means this window
  72.                  * isn't managed by the gridbag). */
  73.     struct GridBag *nextPtr;    /* Next window managed within same
  74.                  * parent.  List is priority-ordered:
  75.                  * first on list gets layed out first. */
  76.     struct GridBag *slavePtr;    /* First in list of slaves managed
  77.                  * inside this window (NULL means
  78.                  * no gridbag slaves). */
  79.  
  80.     int gridColumn, gridRow;
  81.     int gridWidth, gridHeight;
  82.  
  83.     int tempX, tempY;
  84.     int tempWidth, tempHeight;
  85.  
  86.     double weightX, weightY;
  87.     int minWidth, minHeight;
  88.  
  89.     int padX, padY;        /* Total additional pixels to leave around the
  90.                  * window (half of this space is left on each
  91.                  * side).  This is space *outside* the window:
  92.                  * we'll allocate extra space in frame but
  93.                  * won't enlarge window). */
  94.     int iPadX, iPadY;        /* Total extra pixels to allocate inside the
  95.                  * window (half this amount will appear on
  96.                  * each side). */
  97.     int startx, starty;        /* starting location of layout */
  98.  
  99.     int doubleBw;        /* Twice the window's last known border
  100.                  * width.  If this changes, the window
  101.                  * must be re-arranged within its parent. */
  102.     int *abortPtr;        /* If non-NULL, it means that there is a nested
  103.                  * call to ArrangeGrid already working on
  104.                  * this window.  *abortPtr may be set to 1 to
  105.                  * abort that nested call.  This happens, for
  106.                  * example, if tkwin or any of its slaves
  107.                  * is deleted. */
  108.     int flags;            /* Miscellaneous flags;  see below
  109.                  * for definitions. */
  110.  
  111.     Constrain row, column;        /* column and row constraints */
  112.  
  113.     int valid;
  114.     LayoutInfo *layoutCache;
  115. } GridBag;
  116.  
  117. /*
  118.  * Flag values for GridBag structures:
  119.  *
  120.  * REQUESTED_RELAYOUT:        1 means a Tk_DoWhenIdle request
  121.  *                has already been made to re-arrange
  122.  *                all the slaves of this window.
  123.  * STICK_NORTH          1 means this window sticks to the edgth of its
  124.  * STICK_EAST            cavity
  125.  * STICK_SOUTH
  126.  * STICK_WEST
  127.  *
  128.  * DONT_PROPAGATE:        1 means don't set this window's requested
  129.  *                size.  0 means if this window is a master
  130.  *                then Tk will set its requested size to fit
  131.  *                the needs of its slaves.
  132.  */
  133.  
  134. #define STICK_NORTH        1
  135. #define STICK_EAST        2
  136. #define STICK_SOUTH        4
  137. #define STICK_WEST        8
  138. #define STICK_ALL        (STICK_NORTH|STICK_EAST|STICK_SOUTH|STICK_WEST)
  139.  
  140. #define REQUESTED_RELAYOUT    16
  141. #define DONT_PROPAGATE        32
  142.  
  143. /*
  144.  * Hash table used to map from Tk_Window tokens to corresponding
  145.  * GridBag structures:
  146.  */
  147.  
  148. static Tcl_HashTable gridBagHashTable;
  149.  
  150. /*
  151.  * Have statics in this module been initialized?
  152.  */
  153.  
  154. static initialized = 0;
  155.  
  156. /*
  157.  * Prototypes for procedures used only in this file:
  158.  */
  159.  
  160. static void        ArrangeGrid _ANSI_ARGS_((ClientData clientData));
  161. static int        ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
  162.                 Tk_Window tkwin, int argc, char *argv[]));
  163. static void        DestroyGridBag _ANSI_ARGS_((char *memPtr));
  164. static void        GetCachedLayoutInfo _ANSI_ARGS_((GridBag *masterPtr));
  165. static GridBag *    GetGridBag _ANSI_ARGS_((Tk_Window tkwin));
  166. static void        GetLayoutInfo _ANSI_ARGS_((GridBag *masterPtr,
  167.                 LayoutInfo *r));
  168. static void        GetMinSize _ANSI_ARGS_((GridBag *masterPtr,
  169.                 LayoutInfo *info, int *minw, int *minh));
  170. static void        GridBagStructureProc _ANSI_ARGS_((
  171.                 ClientData clientData, XEvent *eventPtr));
  172. static void        GridLostSlaveProc _ANSI_ARGS_((ClientData clientData,
  173.                 Tk_Window tkwin));
  174. static void        GridReqProc _ANSI_ARGS_((ClientData clientData,
  175.                 Tk_Window tkwin));
  176. static void        GridBagStructureProc _ANSI_ARGS_((
  177.                 ClientData clientData, XEvent *eventPtr));
  178. static void        StickyToString _ANSI_ARGS_((int flags, char *result));
  179. static int        StringToSticky _ANSI_ARGS_((char *string));
  180. static void        Unlink _ANSI_ARGS_((GridBag *gridPtr));
  181.  
  182. static Tk_GeomMgr gridMgrType = {
  183.     "grid",            /* name */
  184.     GridReqProc,        /* requestProc */
  185.     GridLostSlaveProc,        /* lostSlaveProc */
  186. };
  187.  
  188. /*
  189.  *--------------------------------------------------------------
  190.  *
  191.  * Tk_GridCmd --
  192.  *
  193.  *    This procedure is invoked to process the "grid" Tcl command.
  194.  *    See the user documentation for details on what it does.
  195.  *
  196.  * Results:
  197.  *    A standard Tcl result.
  198.  *
  199.  * Side effects:
  200.  *    See the user documentation.
  201.  *
  202.  *--------------------------------------------------------------
  203.  */
  204.  
  205. int
  206. Tk_GridCmd(clientData, interp, argc, argv)
  207.     ClientData clientData;    /* Main window associated with
  208.                  * interpreter. */
  209.     Tcl_Interp *interp;        /* Current interpreter. */
  210.     int argc;            /* Number of arguments. */
  211.     char **argv;        /* Argument strings. */
  212. {
  213.     Tk_Window tkwin = (Tk_Window) clientData;
  214.     size_t length;
  215.     char c;
  216.   
  217.     if ((argc >= 2) && (argv[1][0] == '.')) {
  218.     return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
  219.     }
  220.     if (argc < 3) {
  221.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  222.         argv[0], " option arg ?arg ...?\"", (char *) NULL);
  223.     return TCL_ERROR;
  224.     }
  225.     c = argv[1][0];
  226.     length = strlen(argv[1]);
  227.   
  228.     if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
  229.     Tk_Window master;
  230.     GridBag *masterPtr;
  231.     int row, column;
  232.     int i, x, y;
  233.     int prevX, prevY;
  234.     int width, height;
  235.     double weight;
  236.     int diff;
  237.  
  238.     if (argc != 5) {
  239.         Tcl_AppendResult(interp, "Wrong number of arguments: ",
  240.             "must be \"",argv[0],
  241.             " bbox <master> <column> <row>\"", (char *) NULL);
  242.         return TCL_ERROR;
  243.     }
  244.         
  245.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  246.     if (master == NULL) {
  247.         return TCL_ERROR;
  248.     }
  249.     if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) {
  250.         return TCL_ERROR;
  251.     }
  252.     if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) {
  253.         return TCL_ERROR;
  254.     }
  255.     masterPtr = GetGridBag(master);
  256.  
  257.     /* make sure the grid is up to snuff */
  258.  
  259.     while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
  260.         Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
  261.         ArrangeGrid((ClientData) masterPtr);
  262.     }
  263.     GetCachedLayoutInfo(masterPtr);
  264.  
  265.     if (row < 0 || column < 0) {
  266.         *interp->result = '\0';
  267.         return TCL_OK;
  268.     }
  269.     if (column >= masterPtr->layoutCache->width ||
  270.         row >= masterPtr->layoutCache->height) {
  271.         *interp->result = '\0';
  272.         return TCL_OK;
  273.     }
  274.     x = masterPtr->startx;
  275.     y = masterPtr->starty;
  276.     GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
  277.  
  278.     diff = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
  279.     for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++)
  280.         weight += masterPtr->layoutCache->weightX[i];
  281.  
  282.     prevX = 0;            /* Needed to prevent gcc warning. */
  283.     for (i=0; i<=column; i++) {
  284.         int dx = 0;
  285.         if (weight > MINWEIGHT) {
  286.         dx = (int)((((double)diff) * masterPtr->layoutCache->weightX[i])
  287.             / weight);
  288.         }
  289.         prevX = x;
  290.         x += masterPtr->layoutCache->minWidth[i] + dx;
  291.     }
  292.     diff = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
  293.     for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
  294.         weight += masterPtr->layoutCache->weightY[i];
  295.     }
  296.     prevY = 0;            /* Needed to prevent gcc warning. */
  297.     for (i=0; i<=row; i++) {
  298.         int dy = 0;
  299.         if (weight > MINWEIGHT) {
  300.         dy = (int)((((double)diff) * masterPtr->layoutCache->weightY[i])
  301.             / weight);
  302.         }
  303.         prevY = y;
  304.         y += masterPtr->layoutCache->minHeight[i] + dy;
  305.     }
  306.     sprintf(interp->result,"%d %d %d %d",prevX,prevY,x - prevX,y - prevY);
  307.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  308.     if (argv[2][0] != '.') {
  309.         Tcl_AppendResult(interp, "bad argument \"", argv[2],
  310.             "\": must be name of window", (char *) NULL);
  311.         return TCL_ERROR;
  312.     }
  313.     return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
  314.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  315.     Tk_Window slave;
  316.     GridBag *slavePtr;
  317.     int i;
  318.     
  319.     for (i = 2; i < argc; i++) {
  320.         slave = Tk_NameToWindow(interp, argv[i], tkwin);
  321.         if (slave == NULL) {
  322.         return TCL_ERROR;
  323.         }
  324.         slavePtr = GetGridBag(slave);
  325.         if (slavePtr->masterPtr != NULL) {
  326.         Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
  327.             (ClientData) NULL);
  328.         Unlink(slavePtr);
  329.         Tk_UnmapWindow(slavePtr->tkwin);
  330.         }
  331.     }
  332.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  333.     register GridBag *slavePtr;
  334.     Tk_Window slave;
  335.     char buffer[64];
  336.     
  337.     if (argc != 3) {
  338.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  339.             argv[0], " info window\"", (char *) NULL);
  340.         return TCL_ERROR;
  341.     }
  342.     slave = Tk_NameToWindow(interp, argv[2], tkwin);
  343.     if (slave == NULL) {
  344.         return TCL_ERROR;
  345.     }
  346.     slavePtr = GetGridBag(slave);
  347.     if (slavePtr->masterPtr == NULL) {
  348.         interp->result[0] = '\0';
  349.         return TCL_OK;
  350.     }
  351.     
  352.     Tcl_AppendElement(interp, "-in");
  353.     Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
  354.     sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
  355.         slavePtr->gridColumn, slavePtr->gridRow,
  356.         slavePtr->gridWidth, slavePtr->gridHeight);
  357.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  358.     sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
  359.         slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
  360.         slavePtr->padY/2);
  361.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  362.     StickyToString(slavePtr->flags,buffer);
  363.     Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL);
  364. /*
  365.     sprintf(buffer, " -weightx %.2f -weighty %.2f",
  366.         slavePtr->weightX, slavePtr->weightY);
  367.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  368. */
  369.     } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
  370.     Tk_Window master;
  371.     GridBag *masterPtr;
  372.     int propagate;
  373.     
  374.     if (argc > 4) {
  375.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  376.             argv[0], " propagate window ?boolean?\"",
  377.             (char *) NULL);
  378.         return TCL_ERROR;
  379.     }
  380.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  381.     if (master == NULL) {
  382.         return TCL_ERROR;
  383.     }
  384.     masterPtr = GetGridBag(master);
  385.     if (argc == 3) {
  386. #ifdef STk_CODE
  387.         interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "#f" : "#t";
  388. #else
  389.         interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "0" : "1";
  390. #endif
  391.         return TCL_OK;
  392.     }
  393.     if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
  394.         return TCL_ERROR;
  395.     }
  396.     if (propagate) {
  397.         masterPtr->flags &= ~DONT_PROPAGATE;
  398.       
  399.         /*
  400.          * Re-arrange the master to allow new geometry information to
  401.          * propagate upwards to the master\'s master.
  402.          */
  403.       
  404.         if (masterPtr->abortPtr != NULL) {
  405.         *masterPtr->abortPtr = 1;
  406.         }
  407.         masterPtr->valid = 0;
  408.         if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  409.         masterPtr->flags |= REQUESTED_RELAYOUT;
  410.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  411.         }
  412.     } else {
  413.         masterPtr->flags |= DONT_PROPAGATE;
  414.     }
  415.     } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) {
  416.     Tk_Window master;
  417.     GridBag *masterPtr;
  418.  
  419.     if (argc != 3) {
  420.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  421.             argv[0], " size window\"", (char *) NULL);
  422.         return TCL_ERROR;
  423.     }
  424.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  425.     if (master == NULL)
  426.         return TCL_ERROR;
  427.     masterPtr = GetGridBag(master);
  428.     GetCachedLayoutInfo(masterPtr);
  429.  
  430.     sprintf(interp->result, "%d %d", masterPtr->layoutCache->width,
  431.         masterPtr->layoutCache->height);
  432.     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
  433.     Tk_Window master;
  434.     GridBag *masterPtr, *slavePtr;
  435.     int i, value;
  436.     int row = -1, column = -1;
  437.  
  438.     if (argc < 3 || argc%2 ==0) {
  439.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  440.             argv[0], " slaves window ?-option value...?\"",
  441.             (char *) NULL);
  442.         return TCL_ERROR;
  443.     }
  444.  
  445.     for (i=3; i<argc; i+=2) {
  446.         if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
  447.         Tcl_AppendResult(interp, "Invalid args: should be \"",
  448.             argv[0], " slaves window ?-option value...?\"",
  449.             (char *) NULL);
  450.         return TCL_ERROR;
  451.         }
  452.         if (Tcl_GetInt(interp, argv[i+1], &value) != TCL_OK) {
  453.         return TCL_ERROR;
  454.         }
  455.         if (value < 0) {
  456.         Tcl_AppendResult(interp, argv[i],
  457.             " is an invalid value: should NOT be < 0",
  458.             (char *) NULL);
  459.         return TCL_ERROR;
  460.         }
  461.         if (strncmp(argv[i], "-column", length) == 0) {
  462.         column = value;
  463.         } else if (strncmp(argv[i], "-row", length) == 0) {
  464.         row = value;
  465.         } else {
  466.         Tcl_AppendResult(interp, argv[i],
  467.             " is an invalid option: should be \"",
  468.             "-row, -column\"",
  469.             (char *) NULL);
  470.         return TCL_ERROR;
  471.         }
  472.     }
  473.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  474.     if (master == NULL) {
  475.         return TCL_ERROR;
  476.     }
  477.     masterPtr = GetGridBag(master);
  478. #ifdef STk_CODE
  479.     Tcl_AppendResult(interp, "(", NULL);
  480. #endif
  481.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  482.                          slavePtr = slavePtr->nextPtr) {
  483.         if (column>=0 && (slavePtr->gridColumn > column
  484.             || slavePtr->gridColumn+slavePtr->gridWidth-1 < column)) {
  485.         continue;
  486.         }
  487.         if (row>=0 && (slavePtr->gridRow > row ||
  488.             slavePtr->gridRow+slavePtr->gridHeight-1 < row)) {
  489.         continue;
  490.         }
  491.         Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
  492.     }
  493. #ifdef STk_CODE
  494.     Tcl_AppendResult(interp, ")", NULL);
  495. #endif
  496.  
  497.     /*
  498.      * grid columnconfigure <master> <index> -option
  499.      * grid columnconfigure <master> <index> -option value -option value
  500.      * grid rowconfigure <master> <index> -option
  501.      * grid rowconfigure <master> <index> -option value -option value
  502.      */
  503.    
  504.     } else if(((c=='c') && (strncmp(argv[1], "columnconfigure", length) == 0)) ||
  505.             ((c=='r') && (strncmp(argv[1], "rowconfigure", length) == 0))) {
  506.     Tk_Window master;
  507.     GridBag *masterPtr;
  508.     Constrain *con;
  509.     int index, i, size;
  510.     double weight;
  511.  
  512.     if (argc != 5 && (argc < 5 || argc%2 == 1)) {
  513.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  514.             " ", argv[1], " master index ?-option value...?\"",
  515.             (char *)NULL);
  516.         return TCL_ERROR;
  517.     }
  518.  
  519.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  520.     if (master == NULL) {
  521.         return TCL_ERROR;
  522.     }
  523.     masterPtr = GetGridBag(master);
  524.     con = (c=='c') ? &(masterPtr->column) : &(masterPtr->row);
  525.  
  526.     if (Tcl_GetInt(interp, argv[3], &index) != TCL_OK) {
  527.         return TCL_ERROR;
  528.     }
  529.     if (index < 0 || index >= MAXGRIDSIZE) {
  530.         Tcl_AppendResult(interp, argv[3], " is out of range",
  531.             (char *)NULL);
  532.         return TCL_ERROR;
  533.     }
  534.  
  535.     /*
  536.      *  make sure the row/column constraint array is allocated.  This
  537.      *  Should be changed to avoid hard-wired limits.  We'll wimp out
  538.      *  for now.
  539.      */
  540.  
  541.     if (con->max == 0) {
  542.         unsigned int size;
  543.         con->max = MAXGRIDSIZE;
  544.         con->used = 0;
  545.  
  546.         size = MAXGRIDSIZE * sizeof(con->minsize[0]);
  547.         con->minsize = (int *) ckalloc(size);
  548.         memset(con->minsize, 0, size);
  549.  
  550.         size = MAXGRIDSIZE * sizeof(con->weight[0]);
  551.         con->weight = (double *) ckalloc(size);
  552.         memset(con->weight, 0, size);
  553.     }
  554.  
  555.     for (i=4; i<argc; i+=2) {
  556.         if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
  557.         Tcl_AppendResult(interp, "Invalid arg: \"",
  558.             argv[0], "\" expecting -minsize or -weight",
  559.             (char *) NULL);
  560.         return TCL_ERROR;
  561.         }
  562.         if (strncmp(argv[i], "-minsize", length) == 0) {
  563.         if (argc == 5) {
  564.             size = con->used <= index ?  0 : con->minsize[index];
  565.             sprintf(interp->result, "%d", size);
  566.         } else if (Tk_GetPixels(interp, master, argv[i+1], &size)
  567.             != TCL_OK) {
  568.             return TCL_ERROR;
  569.         } else {
  570.             con->minsize[index] = size;
  571.             if (size > 0 && index >= con->used) 
  572.             con->used = index+1;
  573.             else if (size == 0 && index+1 == con->used) {
  574.             while (index >= 0  && (con->minsize[index]==0) &&
  575.                 (con->weight[index] == 0.0)) {
  576.                 index--;
  577.             }
  578.             con->used = index + 1;
  579.             }
  580.         }
  581.         } else if (strncmp(argv[i], "-weight", length) == 0) {
  582.         if (argc == 5) {
  583.             weight = con->used <= index ?  0 : con->weight[index];
  584.             sprintf(interp->result, "%.2f", weight);
  585.         } else if (Tcl_GetDouble(interp, argv[i+1], &weight) != TCL_OK) {
  586.             return TCL_ERROR;
  587.         } else {
  588.             con->weight[index] = weight;
  589.             if (weight > MINWEIGHT && index >= con->used) 
  590.             con->used = index+1;
  591.             else if (weight == 0.0 && index+1 == con->used) {
  592.             while (index >= 0 && (con->minsize[index]==0) &&
  593.                 (con->weight[index] == 0.0)) {
  594.                 index--;
  595.             }
  596.             con->used = index + 1;
  597.             }
  598.         }
  599.         } else {
  600.         Tcl_AppendResult(interp, argv[i],
  601.             " is an invalid option: should be \"",
  602.             "-minsize, -weight\"",
  603.             (char *) NULL);
  604.         return TCL_ERROR;
  605.         }
  606.     }
  607.  
  608.     /* if we changed a property, re-arrange the table */
  609.  
  610.     if (argc != 5) {
  611.         if (masterPtr->abortPtr != NULL) {
  612.         *masterPtr->abortPtr = 1;
  613.         }
  614.         masterPtr->valid = 0;
  615.         if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  616.         masterPtr->flags |= REQUESTED_RELAYOUT;
  617.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  618.         }
  619.     }
  620.     } else if((c == 'l') && (strncmp(argv[1], "location", length) == 0)) {
  621.     Tk_Window master;
  622.     GridBag *masterPtr;
  623.     int x, y, i, j, w, h;
  624.     int width, height;
  625.     double weight;
  626.     int diff;
  627.  
  628.     if (argc != 5) {
  629.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  630.             argv[0], " location master x y\"", (char *)NULL);
  631.         return TCL_ERROR;
  632.     }
  633.  
  634.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  635.     if (master == NULL) {
  636.         return TCL_ERROR;
  637.     }
  638.     masterPtr = GetGridBag(master);
  639.  
  640.     if (Tk_GetPixels(interp, master, argv[3], &x) != TCL_OK) {
  641.         return TCL_ERROR;
  642.     }
  643.     if (Tk_GetPixels(interp, master, argv[4], &y) != TCL_OK) {
  644.         return TCL_ERROR;
  645.     }
  646.  
  647.     /* make sure the grid is up to snuff */
  648.  
  649.     while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
  650.         Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
  651.         ArrangeGrid((ClientData) masterPtr);
  652.     }
  653.     GetCachedLayoutInfo(masterPtr);
  654.     GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
  655.  
  656.     diff = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
  657.     for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
  658.         weight += masterPtr->layoutCache->weightX[i];
  659.     }
  660.     w = masterPtr->startx;
  661.     if (w > x) {
  662.         i = -1;
  663.     } else {
  664.         for (i=0; i<masterPtr->layoutCache->width; i++) {
  665.         int dx = 0;
  666.         if (weight > MINWEIGHT) {
  667.             dx = (int)((((double)diff) * masterPtr->layoutCache->weightX[i])
  668.                 / weight);
  669.             }
  670.         w += masterPtr->layoutCache->minWidth[i] + dx;
  671.         if (w > x) {
  672.             break;
  673.         }
  674.         }
  675.     }
  676.  
  677.     diff = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
  678.     for (weight=0.0, j = 0; j < masterPtr->layoutCache->height; j++)
  679.         weight += masterPtr->layoutCache->weightY[j];
  680.     h = masterPtr->starty;
  681.     if (h > y) {
  682.         j = -1;
  683.     } else {
  684.         for (j=0; j<masterPtr->layoutCache->height; j++) {
  685.         int dy = 0;
  686.         if (weight > MINWEIGHT) {
  687.             dy = (int)((((double)diff) * masterPtr->layoutCache->weightY[j])
  688.                 / weight);
  689.         }
  690.         h += masterPtr->layoutCache->minHeight[j] + dy;
  691.         if (h > y) {
  692.             break;
  693.         }
  694.         }
  695.     }
  696.     sprintf(interp->result, "%d %d", i, j);
  697.     } else {
  698.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  699.         "\":  must be bbox, columnconfigure, configure, forget, info, ",
  700.         "location, propagate, rowconfigure, size, or slaves",
  701.         (char *) NULL);
  702.     return TCL_ERROR;
  703.     }
  704.     return TCL_OK;
  705. }
  706.  
  707. /*
  708.  *--------------------------------------------------------------
  709.  *
  710.  * GridReqProc --
  711.  *
  712.  *    This procedure is invoked by Tk_GeometryRequest for
  713.  *    windows managed by the gridbag.
  714.  *
  715.  * Results:
  716.  *    None.
  717.  *
  718.  * Side effects:
  719.  *    Arranges for tkwin, and all its managed siblings, to
  720.  *    be re-arranged at the next idle point.
  721.  *
  722.  *--------------------------------------------------------------
  723.  */
  724.  
  725. /* ARGSUSED */
  726. static void
  727. GridReqProc(clientData, tkwin)
  728.     ClientData clientData;    /* GridBag's information about
  729.                  * window that got new preferred
  730.                  * geometry.  */
  731.     Tk_Window tkwin;        /* Other Tk-related information
  732.                  * about the window. */
  733. {
  734.     register GridBag *gridPtr = (GridBag *) clientData;
  735.  
  736.     gridPtr = gridPtr->masterPtr;
  737.     gridPtr->valid = 0;
  738.     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
  739.     gridPtr->flags |= REQUESTED_RELAYOUT;
  740.     Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
  741.     }
  742. }
  743.  
  744.  
  745. /*
  746.  *--------------------------------------------------------------
  747.  *
  748.  * GridLostSlaveProc --
  749.  *
  750.  *    This procedure is invoked by Tk whenever some other geometry
  751.  *    claims control over a slave that used to be managed by us.
  752.  *
  753.  * Results:
  754.  *    None.
  755.  *
  756.  * Side effects:
  757.  *    Forgets all grid-related information about the slave.
  758.  *
  759.  *--------------------------------------------------------------
  760.  */
  761.  
  762.     /* ARGSUSED */
  763. static void
  764. GridLostSlaveProc(clientData, tkwin)
  765.     ClientData clientData;    /* GridBag structure for slave window that
  766.                  * was stolen away. */
  767.     Tk_Window tkwin;        /* Tk's handle for the slave window. */
  768. {
  769.     register GridBag *slavePtr = (GridBag *) clientData;
  770.  
  771.     if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
  772.     Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
  773.     }
  774.     Unlink(slavePtr);
  775.     Tk_UnmapWindow(slavePtr->tkwin);
  776. }
  777.  
  778. /*
  779.  * Fill in an instance of the above structure for the current set
  780.  * of managed children.  This requires two passes through the
  781.  * set of children, first to figure out what cells they occupy
  782.  * and how many rows and columns there are, and then to distribute
  783.  * the weights and min sizes amoung the rows/columns.
  784.  *
  785.  * This also caches the minsizes for all the children when they are
  786.  * first encountered.
  787.  */
  788.  
  789. static void
  790. GetLayoutInfo(masterPtr, r)
  791.     GridBag *masterPtr;
  792.     LayoutInfo *r;
  793. {
  794.     register GridBag *slavePtr;
  795.     int i, k, px, py, pixels_diff, nextSize;
  796.     double weight_diff, weight;
  797.     register int curX, curY, curWidth, curHeight, curRow, curCol;
  798.     int xMax[MAXGRIDSIZE];
  799.     int yMax[MAXGRIDSIZE];
  800.  
  801.     /*
  802.      * Pass #1
  803.      *
  804.      * Figure out the dimensions of the layout grid.
  805.      */
  806.  
  807.     r->width = r->height = 0;
  808.     curRow = curCol = -1;
  809.     memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
  810.     memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
  811.  
  812.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  813.                      slavePtr = slavePtr->nextPtr) {
  814.  
  815.     curX = slavePtr->gridColumn;
  816.     curY = slavePtr->gridRow;
  817.     curWidth = slavePtr->gridWidth;
  818.     curHeight = slavePtr->gridHeight;
  819.  
  820.     /* Adjust the grid width and height */
  821.     for (px = curX + curWidth; r->width < px; r->width++) {
  822.         /* Null body. */
  823.     }
  824.     for (py = curY + curHeight; r->height < py; r->height++) {
  825.         /* Null body. */
  826.     }
  827.  
  828.     /* Adjust the xMax and yMax arrays */
  829.     for (i = curX; i < (curX + curWidth); i++) {
  830.         yMax[i] = py;
  831.     }
  832.     for (i = curY; i < (curY + curHeight); i++) {
  833.         xMax[i] = px;
  834.     }
  835.  
  836.     /* Cache the current slave's size. */
  837.     slavePtr->minWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw;
  838.     slavePtr->minHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw;
  839.     }
  840.  
  841.     /*
  842.      * Apply minimum row/column dimensions
  843.      */ 
  844.     if (r->width < masterPtr->column.used) {
  845.     r->width = masterPtr->column.used;
  846.     }
  847.     r->lastRow = r->height;
  848.     if (r->height < masterPtr->row.used) {
  849.     r->height = masterPtr->row.used;
  850.     }
  851.  
  852.     /*
  853.      * Pass #2
  854.      */
  855.  
  856.     curRow = curCol = -1;
  857.     memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
  858.     memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
  859.  
  860.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  861.                      slavePtr = slavePtr->nextPtr) {
  862.     curX = slavePtr->gridColumn;
  863.     curY = slavePtr->gridRow;
  864.     curWidth = slavePtr->gridWidth;
  865.     curHeight = slavePtr->gridHeight;
  866.  
  867.     px = curX + curWidth;
  868.     py = curY + curHeight;
  869.  
  870.     for (i = curX; i < (curX + curWidth); i++) {
  871.         yMax[i] = py;
  872.     }
  873.     for (i = curY; i < (curY + curHeight); i++) {
  874.         xMax[i] = px;
  875.     }
  876.  
  877.     /* Assign the new values to the gridbag slave */
  878.     slavePtr->tempX = curX;
  879.     slavePtr->tempY = curY;
  880.     slavePtr->tempWidth = curWidth;
  881.     slavePtr->tempHeight = curHeight;
  882.     }
  883.  
  884.     /*
  885.      * Pass #3
  886.      *
  887.      * Distribute the minimun widths and weights:
  888.      */
  889.  
  890.     /* Initialize arrays to zero */
  891.     memset(r->minWidth, 0, r->width * sizeof(int));
  892.     memset(r->minHeight, 0, r->height * sizeof(int));
  893.     memset(r->weightX, 0, r->width * sizeof(double));
  894.     memset(r->weightY, 0, r->height * sizeof(double));
  895.     nextSize = MAXINT;
  896.  
  897.     for (i = 1; i != MAXINT; i = nextSize, nextSize = MAXINT) {
  898.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  899.                          slavePtr = slavePtr->nextPtr) {
  900.  
  901.         if (slavePtr->tempWidth == i) {
  902.         px = slavePtr->tempX + slavePtr->tempWidth; /* right column */
  903.  
  904.         /* 
  905.          * Figure out if we should use this slave\'s weight.  If the weight
  906.          * is less than the total weight spanned by the width of the cell,
  907.          * then discard the weight.  Otherwise split it the difference
  908.          * according to the existing weights.
  909.          */
  910.  
  911.         weight_diff = slavePtr->weightX;
  912.         for (k = slavePtr->tempX; k < px; k++)
  913.             weight_diff -= r->weightX[k];
  914.         if (weight_diff > 0.0) {
  915.             weight = 0.0;
  916.             for (k = slavePtr->tempX; k < px; k++)
  917.             weight += r->weightX[k];
  918.             for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
  919.             double wt = r->weightX[k];
  920.             double dx = (wt * weight_diff) / weight;
  921.             r->weightX[k] += dx;
  922.             weight_diff -= dx;
  923.             weight -= wt;
  924.             }
  925.             /* Assign the remainder to the rightmost cell */
  926.             r->weightX[px-1] += weight_diff;
  927.         }
  928.  
  929.         /*
  930.          * Calculate the minWidth array values.
  931.          * First, figure out how wide the current slave needs to be.
  932.          * Then, see if it will fit within the current minWidth values.
  933.          * If it won\'t fit, add the difference according to the weightX array.
  934.          */
  935.  
  936.         pixels_diff = slavePtr->minWidth + slavePtr->padX + slavePtr->iPadX;
  937.         for (k = slavePtr->tempX; k < px; k++)
  938.             pixels_diff -= r->minWidth[k];
  939.         if (pixels_diff > 0) {
  940.             weight = 0.0;
  941.             for (k = slavePtr->tempX; k < px; k++)
  942.             weight += r->weightX[k];
  943.             for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
  944.             double wt = r->weightX[k];
  945.             int dx = (int)((wt * ((double)pixels_diff)) / weight);
  946.             r->minWidth[k] += dx;
  947.             pixels_diff -= dx;
  948.             weight -= wt;
  949.             }
  950.             /* Any leftovers go into the rightmost cell */
  951.             r->minWidth[px-1] += pixels_diff;
  952.         }
  953.         }
  954.         else if (slavePtr->tempWidth > i && slavePtr->tempWidth < nextSize)
  955.         nextSize = slavePtr->tempWidth;
  956.  
  957.  
  958.         if (slavePtr->tempHeight == i) {
  959.         py = slavePtr->tempY + slavePtr->tempHeight; /* bottom row */
  960.  
  961.         /* 
  962.          * Figure out if we should use this slave\'s weight.  If the weight
  963.          * is less than the total weight spanned by the height of the cell,
  964.          * then discard the weight.  Otherwise split it the difference
  965.          * according to the existing weights.
  966.          */
  967.  
  968.         weight_diff = slavePtr->weightY;
  969.         for (k = slavePtr->tempY; k < py; k++)
  970.             weight_diff -= r->weightY[k];
  971.         if (weight_diff > 0.0) {
  972.             weight = 0.0;
  973.             for (k = slavePtr->tempY; k < py; k++)
  974.             weight += r->weightY[k];
  975.             for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
  976.             double wt = r->weightY[k];
  977.             double dy = (wt * weight_diff) / weight;
  978.             r->weightY[k] += dy;
  979.             weight_diff -= dy;
  980.             weight -= wt;
  981.             }
  982.             /* Assign the remainder to the bottom cell */
  983.             r->weightY[py-1] += weight_diff;
  984.         }
  985.  
  986.         /*
  987.          * Calculate the minHeight array values.
  988.          * First, figure out how tall the current slave needs to be.
  989.          * Then, see if it will fit within the current minHeight values.
  990.          * If it won\'t fit, add the difference according to the weightY array.
  991.          */
  992.  
  993.         pixels_diff = slavePtr->minHeight + slavePtr->padY + slavePtr->iPadY;
  994.         for (k = slavePtr->tempY; k < py; k++)
  995.             pixels_diff -= r->minHeight[k];
  996.         if (pixels_diff > 0) {
  997.             weight = 0.0;
  998.             for (k = slavePtr->tempY; k < py; k++)
  999.             weight += r->weightY[k];
  1000.             for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
  1001.             double wt = r->weightY[k];
  1002.             int dy = (int)((wt * ((double)pixels_diff)) / weight);
  1003.             r->minHeight[k] += dy;
  1004.             pixels_diff -= dy;
  1005.             weight -= wt;
  1006.             }
  1007.             /* Any leftovers go into the bottom cell */
  1008.             r->minHeight[py-1] += pixels_diff;
  1009.         }
  1010.         }
  1011.         else if (slavePtr->tempHeight > i && slavePtr->tempHeight < nextSize)
  1012.         nextSize = slavePtr->tempHeight;
  1013.     }
  1014.     }
  1015.  
  1016.     /*
  1017.      * Apply minimum row/column dimensions
  1018.      */
  1019.     for (i=0; i<masterPtr->column.used; i++) {
  1020.     if (r->minWidth[i] < masterPtr->column.minsize[i])
  1021.         r->minWidth[i] = masterPtr->column.minsize[i];
  1022.     if (r->weightX[i] < masterPtr->column.weight[i])
  1023.         r->weightX[i] = masterPtr->column.weight[i];
  1024.     }
  1025.     for (i=0; i<masterPtr->row.used; i++) {
  1026.     if (r->minHeight[i] < masterPtr->row.minsize[i])
  1027.         r->minHeight[i] = masterPtr->row.minsize[i];
  1028.     if (r->weightY[i] < masterPtr->row.weight[i])
  1029.         r->weightY[i] = masterPtr->row.weight[i];
  1030.     }
  1031. }
  1032.  
  1033. /*
  1034.  * Cache the layout info after it is calculated.
  1035.  */
  1036. static void
  1037. GetCachedLayoutInfo(masterPtr)
  1038.     GridBag *masterPtr;
  1039. {
  1040.     if (masterPtr->valid == 0) {
  1041.     if (!masterPtr->layoutCache)
  1042.         masterPtr->layoutCache = (LayoutInfo *)ckalloc(sizeof(LayoutInfo));
  1043.  
  1044.     GetLayoutInfo(masterPtr, masterPtr->layoutCache);
  1045.     masterPtr->valid = 1;
  1046.     }
  1047. }
  1048.  
  1049. /*
  1050.  * Adjusts the x, y, width, and height fields to the correct
  1051.  * values depending on the constraint geometry and pads.
  1052.  */
  1053.  
  1054. static void
  1055. AdjustForGravity(gridPtr, x, y, width, height)
  1056.     GridBag *gridPtr;
  1057.     int *x;
  1058.     int *y;
  1059.     int *width;
  1060.     int *height;
  1061. {
  1062.     int diffx=0, diffy=0;
  1063.     int sticky = gridPtr->flags&STICK_ALL;
  1064.  
  1065.     *x += gridPtr->padX/2;
  1066.     *width -= gridPtr->padX;
  1067.     *y += gridPtr->padY/2;
  1068.     *height -= gridPtr->padY;
  1069.  
  1070.     if (*width > (gridPtr->minWidth + gridPtr->iPadX)) {
  1071.     diffx = *width - (gridPtr->minWidth + gridPtr->iPadX);
  1072.     *width = gridPtr->minWidth + gridPtr->iPadX;
  1073.     }
  1074.  
  1075.     if (*height > (gridPtr->minHeight + gridPtr->iPadY)) {
  1076.     diffy = *height - (gridPtr->minHeight + gridPtr->iPadY);
  1077.     *height = gridPtr->minHeight + gridPtr->iPadY;
  1078.     }
  1079.  
  1080.     if (sticky&STICK_EAST && sticky&STICK_WEST)
  1081.     *width += diffx;
  1082.     if (sticky&STICK_NORTH && sticky&STICK_SOUTH)
  1083.     *height += diffy;
  1084.     if (!(sticky&STICK_WEST)) {
  1085.     if (sticky&STICK_EAST)
  1086.         *x += diffx;
  1087.     else
  1088.         *x += diffx/2;
  1089.     }
  1090.     if (!(sticky&STICK_NORTH)) {
  1091.     if (sticky&STICK_SOUTH)
  1092.         *y += diffy;
  1093.     else
  1094.         *y += diffy/2;
  1095.     }
  1096. }
  1097.  
  1098. /*
  1099.  * Figure out the minimum size (not counting the X border) of the
  1100.  * master based on the information from GetLayoutInfo()
  1101.  */
  1102.  
  1103. static void
  1104. GetMinSize(masterPtr, info, minw, minh)
  1105.     GridBag *masterPtr;
  1106.     LayoutInfo *info;
  1107.     int *minw;
  1108.     int *minh;
  1109. {
  1110.     int i, t;
  1111.     int intBWidth;    /* Width of internal border in parent window,
  1112.              * if any. */
  1113.  
  1114.     intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
  1115.  
  1116.     t = 0;
  1117.     for(i = 0; i < info->width; i++)
  1118.     t += info->minWidth[i];
  1119.     *minw = t + 2*intBWidth;
  1120.  
  1121.     t = 0;
  1122.     for(i = 0; i < info->height; i++)
  1123.     t += info->minHeight[i];
  1124.     *minh = t + 2*intBWidth;
  1125. }
  1126.  
  1127. /*
  1128.  *--------------------------------------------------------------
  1129.  *
  1130.  * ArrangeGrid --
  1131.  *
  1132.  *    This procedure is invoked (using the Tk_DoWhenIdle
  1133.  *    mechanism) to re-layout a set of windows managed by
  1134.  *    the gridbag.  It is invoked at idle time so that a
  1135.  *    series of gridbag requests can be merged into a single
  1136.  *    layout operation.
  1137.  *
  1138.  * Results:
  1139.  *    None.
  1140.  *
  1141.  * Side effects:
  1142.  *    The slaves of masterPtr may get resized or moved.
  1143.  *
  1144.  *--------------------------------------------------------------
  1145.  */
  1146.  
  1147. static void
  1148. ArrangeGrid(clientData)
  1149.     ClientData clientData;    /* Structure describing parent whose slaves
  1150.                  * are to be re-layed out. */
  1151. {
  1152.     register GridBag *masterPtr = (GridBag *) clientData;
  1153.     register GridBag *slavePtr;    
  1154.     int abort;
  1155.     int i, x, y, width, height;
  1156.     int diffw, diffh;
  1157.     double weight;
  1158.     Tk_Window parent, ancestor;
  1159.     LayoutInfo info;
  1160.     int intBWidth;    /* Width of internal border in parent window,
  1161.              * if any. */
  1162.     int iPadX, iPadY;
  1163.  
  1164.     masterPtr->flags &= ~REQUESTED_RELAYOUT;
  1165.  
  1166.     /*
  1167.      * If the parent has no slaves anymore, then don't do anything
  1168.      * at all:  just leave the parent's size as-is.
  1169.      * Even if row and column constraints have been set!
  1170.      */
  1171.  
  1172.     if (masterPtr->slavePtr == NULL) {
  1173.     return;
  1174.     }
  1175.  
  1176.     /*
  1177.      * Abort any nested call to ArrangeGrid for this window, since
  1178.      * we'll do everything necessary here, and set up so this call
  1179.      * can be aborted if necessary.  
  1180.      */
  1181.  
  1182.     if (masterPtr->abortPtr != NULL) {
  1183.     *masterPtr->abortPtr = 1;
  1184.     }
  1185.     masterPtr->abortPtr = &abort;
  1186.     abort = 0;
  1187.     Tk_Preserve((ClientData) masterPtr);
  1188.  
  1189.     /*
  1190.      * Pass #1: scan all the slaves to figure out the total amount
  1191.      * of space needed.
  1192.      */
  1193.  
  1194.     GetLayoutInfo(masterPtr, &info);
  1195.     GetMinSize(masterPtr, &info, &width, &height);
  1196.  
  1197.     if (((width != Tk_ReqWidth(masterPtr->tkwin))
  1198.         || (height != Tk_ReqHeight(masterPtr->tkwin)))
  1199.         && !(masterPtr->flags & DONT_PROPAGATE)) {
  1200.     Tk_GeometryRequest(masterPtr->tkwin, width, height);
  1201.     masterPtr->flags |= REQUESTED_RELAYOUT;
  1202.     masterPtr->valid = 0;
  1203.     Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  1204.     goto done;
  1205.     }
  1206.  
  1207.     /*
  1208.      * If the parent isn't mapped then don't do anything more:  wait
  1209.      * until it gets mapped again.  Need to get at least to here to
  1210.      * reflect size needs up the window hierarchy, but there's no
  1211.      * point in actually mapping the slaves.
  1212.      */
  1213.  
  1214.     if (!Tk_IsMapped(masterPtr->tkwin)) {
  1215.     goto done;
  1216.     }
  1217.  
  1218.  
  1219.     /*
  1220.      * If the current dimensions of the window don't match the desired
  1221.      * dimensions, then adjust the minWidth and minHeight arrays
  1222.      * according to the weights.
  1223.      */
  1224.  
  1225.     diffw = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
  1226.     if (diffw != 0) {
  1227.     weight = 0.0;
  1228.     for (i = 0; i < info.width; i++)
  1229.         weight += info.weightX[i];
  1230.     if (weight > MINWEIGHT) {
  1231.         for (i = 0; i < info.width; i++) {
  1232.         int dx = (int)(( ((double)diffw) * info.weightX[i]) / weight);
  1233.         info.minWidth[i] += dx;
  1234.         width += dx;
  1235.         if (info.minWidth[i] < 0) {
  1236.             width -= info.minWidth[i];
  1237.             info.minWidth[i] = 0;
  1238.         }
  1239.         }
  1240.     }
  1241.     diffw = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
  1242.     }
  1243.     else {
  1244.     diffw = 0;
  1245.     }
  1246.  
  1247.     diffh = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
  1248.     if (diffh != 0) {
  1249.     weight = 0.0;
  1250.     for (i = 0; i < info.height; i++)
  1251.         weight += info.weightY[i];
  1252.     if (weight > MINWEIGHT) {
  1253.         for (i = 0; i < info.height; i++) {
  1254.         int dy = (int)(( ((double)diffh) * info.weightY[i]) / weight);
  1255.         info.minHeight[i] += dy;
  1256.         height += dy;
  1257.         if (info.minHeight[i] < 0) {
  1258.             height -= info.minHeight[i];
  1259.             info.minHeight[i] = 0;
  1260.         }
  1261.         }
  1262.     }
  1263.     diffh = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
  1264.     }
  1265.     else {
  1266.     diffh = 0;
  1267.     }
  1268.  
  1269.     /*
  1270.      * Now do the actual layout of the slaves using the layout information
  1271.      * that has been collected.
  1272.      */
  1273.  
  1274.     iPadX = masterPtr->iPadX/2;
  1275.     iPadY = masterPtr->iPadY/2;
  1276.     intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
  1277.  
  1278.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  1279.                      slavePtr = slavePtr->nextPtr) {
  1280.  
  1281.     masterPtr->startx = x = diffw/2 + intBWidth + iPadX;
  1282.     for(i = 0; i < slavePtr->tempX; i++)
  1283.         x += info.minWidth[i];
  1284.  
  1285.     masterPtr->starty = y = diffh/2 + intBWidth + iPadY;
  1286.     for(i = 0; i < slavePtr->tempY; i++)
  1287.         y += info.minHeight[i];
  1288.  
  1289.     width = 0;
  1290.     for(i = slavePtr->tempX; i < (slavePtr->tempX + slavePtr->tempWidth); i++)
  1291.         width += info.minWidth[i];
  1292.  
  1293.     height = 0;
  1294.     for(i = slavePtr->tempY; i < (slavePtr->tempY + slavePtr->tempHeight); i++)
  1295.         height += info.minHeight[i];
  1296.  
  1297.     AdjustForGravity(slavePtr, &x, &y, &width, &height);
  1298.  
  1299.     /*
  1300.      * If the window in which slavePtr is managed is not its
  1301.      * parent in the window hierarchy, translate the coordinates
  1302.      * to the coordinate system of the real X parent.
  1303.      */
  1304.  
  1305.     parent = Tk_Parent(slavePtr->tkwin);
  1306.     for (ancestor = masterPtr->tkwin; ancestor != parent;
  1307.                       ancestor = Tk_Parent(ancestor)) {
  1308.         x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  1309.         y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  1310.     }
  1311.  
  1312.     /*
  1313.      * If the window is too small to be interesting then
  1314.      * unmap it.  Otherwise configure it and then make sure
  1315.      * it's mapped.
  1316.      */
  1317.  
  1318.     if ((width <= 0) || (height <= 0)) {
  1319.         Tk_UnmapWindow(slavePtr->tkwin);
  1320.     }
  1321.     else {
  1322.         if ((x != Tk_X(slavePtr->tkwin))
  1323.             || (y != Tk_Y(slavePtr->tkwin))
  1324.             || (width != Tk_Width(slavePtr->tkwin))
  1325.             || (height != Tk_Height(slavePtr->tkwin))) {
  1326.         Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
  1327.         }
  1328.         if (abort) {
  1329.         goto done;
  1330.         }
  1331.         Tk_MapWindow(slavePtr->tkwin);
  1332.     }
  1333.  
  1334.     /*
  1335.      * Changes to the window's structure could cause almost anything
  1336.      * to happen, including deleting the parent or child.  If this
  1337.      * happens, we'll be told to abort.
  1338.      */
  1339.  
  1340.     if (abort) {
  1341.         goto done;
  1342.     }
  1343.     }
  1344.  
  1345.     done:
  1346.     masterPtr->abortPtr = NULL;
  1347.     Tk_Release((ClientData) masterPtr);
  1348. }
  1349.  
  1350.  
  1351.  
  1352. /*
  1353.  *--------------------------------------------------------------
  1354.  *
  1355.  * GetGridBag --
  1356.  *
  1357.  *    This internal procedure is used to locate a GridBag
  1358.  *    structure for a given window, creating one if one
  1359.  *    doesn't exist already.
  1360.  *
  1361.  * Results:
  1362.  *    The return value is a pointer to the GridBag structure
  1363.  *    corresponding to tkwin.
  1364.  *
  1365.  * Side effects:
  1366.  *    A new gridbag structure may be created.  If so, then
  1367.  *    a callback is set up to clean things up when the
  1368.  *    window is deleted.
  1369.  *
  1370.  *--------------------------------------------------------------
  1371.  */
  1372.  
  1373. static GridBag *
  1374. GetGridBag(tkwin)
  1375.     Tk_Window tkwin;        /* Token for window for which
  1376.                  * gridbag structure is desired. */
  1377. {
  1378.     register GridBag *gridPtr;
  1379.     Tcl_HashEntry *hPtr;
  1380.     int new;
  1381.  
  1382.     if (!initialized) {
  1383.     initialized = 1;
  1384.     Tcl_InitHashTable(&gridBagHashTable, TCL_ONE_WORD_KEYS);
  1385.     }
  1386.  
  1387.     /*
  1388.      * See if there's already gridbag for this window.  If not,
  1389.      * then create a new one.
  1390.      */
  1391.  
  1392.     hPtr = Tcl_CreateHashEntry(&gridBagHashTable, (char *) tkwin, &new);
  1393.     if (!new) {
  1394.     return (GridBag *) Tcl_GetHashValue(hPtr);
  1395.     }
  1396.     gridPtr = (GridBag *) ckalloc(sizeof(GridBag));
  1397.     gridPtr->tkwin = tkwin;
  1398.     gridPtr->masterPtr = NULL;
  1399.     gridPtr->nextPtr = NULL;
  1400.     gridPtr->slavePtr = NULL;
  1401.  
  1402.     gridPtr->gridColumn = gridPtr->gridRow = -1;
  1403.     gridPtr->gridWidth = gridPtr->gridHeight = 1;
  1404.     gridPtr->weightX = gridPtr->weightY = 0.0;
  1405.     gridPtr->minWidth = gridPtr->minHeight = 0;
  1406.  
  1407.     gridPtr->padX = gridPtr->padY = 0;
  1408.     gridPtr->iPadX = gridPtr->iPadY = 0;
  1409.     gridPtr->startx = gridPtr->starty = 0;
  1410.     gridPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
  1411.     gridPtr->abortPtr = NULL;
  1412.     gridPtr->flags = 0;
  1413.  
  1414.     gridPtr->column.max = 0;
  1415.     gridPtr->row.max = 0;
  1416.     gridPtr->column.used = 0;
  1417.     gridPtr->row.used = 0;
  1418.  
  1419.     gridPtr->valid = 0;
  1420.     gridPtr->layoutCache = NULL;
  1421.  
  1422.     Tcl_SetHashValue(hPtr, gridPtr);
  1423.     Tk_CreateEventHandler(tkwin, StructureNotifyMask,
  1424.         GridBagStructureProc, (ClientData) gridPtr);
  1425.     return gridPtr;
  1426. }
  1427.  
  1428. /*
  1429.  *----------------------------------------------------------------------
  1430.  *
  1431.  * Unlink --
  1432.  *
  1433.  *    Remove a gridbag from its parent's list of slaves.
  1434.  *
  1435.  * Results:
  1436.  *    None.
  1437.  *
  1438.  * Side effects:
  1439.  *    The parent will be scheduled for re-arranging.
  1440.  *
  1441.  *----------------------------------------------------------------------
  1442.  */
  1443.  
  1444. static void
  1445. Unlink(gridPtr)
  1446.     register GridBag *gridPtr;        /* Window to unlink. */
  1447. {
  1448.     register GridBag *masterPtr, *gridPtr2;
  1449.  
  1450.     masterPtr = gridPtr->masterPtr;
  1451.     if (masterPtr == NULL) {
  1452.     return;
  1453.     }
  1454.     if (masterPtr->slavePtr == gridPtr) {
  1455.     masterPtr->slavePtr = gridPtr->nextPtr;
  1456.     }
  1457.     else {
  1458.     for (gridPtr2 = masterPtr->slavePtr; ; gridPtr2 = gridPtr2->nextPtr) {
  1459.         if (gridPtr2 == NULL) {
  1460.         panic("Unlink couldn't find previous window");
  1461.         }
  1462.         if (gridPtr2->nextPtr == gridPtr) {
  1463.         gridPtr2->nextPtr = gridPtr->nextPtr;
  1464.         break;
  1465.         }
  1466.     }
  1467.     }
  1468.     masterPtr->valid = 0;
  1469.     if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  1470.     masterPtr->flags |= REQUESTED_RELAYOUT;
  1471.     Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  1472.     }
  1473.     if (masterPtr->abortPtr != NULL) {
  1474.     *masterPtr->abortPtr = 1;
  1475.     }
  1476.  
  1477.     gridPtr->masterPtr = NULL;
  1478. }
  1479.  
  1480.  
  1481.  
  1482. /*
  1483.  *----------------------------------------------------------------------
  1484.  *
  1485.  * DestroyGridBag --
  1486.  *
  1487.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  1488.  *    to clean up the internal structure of a gridbag at a safe time
  1489.  *    (when no-one is using it anymore).
  1490.  *
  1491.  * Results:
  1492.  *    None.
  1493.  *
  1494.  * Side effects:
  1495.  *    Everything associated with the gridbag is freed up.
  1496.  *
  1497.  *----------------------------------------------------------------------
  1498.  */
  1499.  
  1500. static void
  1501. DestroyGridBag(memPtr)
  1502.     char *memPtr;        /* Info about window that is now dead. */
  1503. {
  1504.     register GridBag *gridPtr = (GridBag *) memPtr;
  1505.  
  1506.     if (gridPtr->column.max) {
  1507.     ckfree((char *) gridPtr->column.minsize);
  1508.     ckfree((char *) gridPtr->column.weight);
  1509.     }
  1510.     if (gridPtr->row.max) {
  1511.     ckfree((char *) gridPtr->row.minsize);
  1512.     ckfree((char *) gridPtr->row.weight);
  1513.     }
  1514.     if (gridPtr->layoutCache)
  1515.     ckfree((char *) gridPtr->layoutCache);
  1516.  
  1517.     ckfree((char *) gridPtr);
  1518. }
  1519.  
  1520. /*
  1521.  *----------------------------------------------------------------------
  1522.  *
  1523.  * GridBagStructureProc --
  1524.  *
  1525.  *    This procedure is invoked by the Tk event dispatcher in response
  1526.  *    to StructureNotify events.
  1527.  *
  1528.  * Results:
  1529.  *    None.
  1530.  *
  1531.  * Side effects:
  1532.  *    If a window was just deleted, clean up all its gridbag-related
  1533.  *    information.  If it was just resized, re-configure its slaves, if
  1534.  *    any.
  1535.  *
  1536.  *----------------------------------------------------------------------
  1537.  */
  1538.  
  1539. static void
  1540. GridBagStructureProc(clientData, eventPtr)
  1541.     ClientData clientData;        /* Our information about window
  1542.                      * referred to by eventPtr. */
  1543.     XEvent *eventPtr;            /* Describes what just happened. */
  1544. {
  1545.     register GridBag *gridPtr = (GridBag *) clientData;
  1546.  
  1547.     if (eventPtr->type == ConfigureNotify) {
  1548.     gridPtr->valid = 0;
  1549.     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
  1550.         gridPtr->flags |= REQUESTED_RELAYOUT;
  1551.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
  1552.     }
  1553.     if (gridPtr->doubleBw != 2*Tk_Changes(gridPtr->tkwin)->border_width) {
  1554.         if ((gridPtr->masterPtr != NULL) &&
  1555.             !(gridPtr->masterPtr->flags & REQUESTED_RELAYOUT)) {
  1556.         gridPtr->doubleBw = 2*Tk_Changes(gridPtr->tkwin)->border_width;
  1557.         gridPtr->masterPtr->flags |= REQUESTED_RELAYOUT;
  1558.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr->masterPtr);
  1559.         }
  1560.     }
  1561.     }
  1562.     else if (eventPtr->type == DestroyNotify) {
  1563.     register GridBag *gridPtr2, *nextPtr;
  1564.  
  1565.     if (gridPtr->masterPtr != NULL) {
  1566.         Unlink(gridPtr);
  1567.     }
  1568.     for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
  1569.                        gridPtr2 = nextPtr) {
  1570.         Tk_UnmapWindow(gridPtr2->tkwin);
  1571.         gridPtr2->masterPtr = NULL;
  1572.         nextPtr = gridPtr2->nextPtr;
  1573.         gridPtr2->nextPtr = NULL;
  1574.     }
  1575.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&gridBagHashTable,
  1576.         (char *) gridPtr->tkwin));
  1577.     if (gridPtr->flags & REQUESTED_RELAYOUT) {
  1578.         Tk_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
  1579.     }
  1580.     gridPtr->tkwin = NULL;
  1581.     Tk_EventuallyFree((ClientData) gridPtr, DestroyGridBag);
  1582.     }
  1583.     else if (eventPtr->type == MapNotify) {
  1584.     gridPtr->valid = 0;
  1585.     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
  1586.         gridPtr->flags |= REQUESTED_RELAYOUT;
  1587.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
  1588.     }
  1589.     }
  1590.     else if (eventPtr->type == UnmapNotify) {
  1591.     register GridBag *gridPtr2;
  1592.  
  1593.     for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
  1594.                        gridPtr2 = gridPtr2->nextPtr) {
  1595.         Tk_UnmapWindow(gridPtr2->tkwin);
  1596.     }
  1597.     }
  1598. }
  1599.  
  1600.  
  1601.  
  1602. /*
  1603.  *----------------------------------------------------------------------
  1604.  *
  1605.  * ConfigureSlaves --
  1606.  *
  1607.  *    This implements the guts of the "grid configure" command.  Given
  1608.  *    a list of slaves and configuration options, it arranges for the
  1609.  *    gridbag to manage the slaves and sets the specified options.
  1610.  *
  1611.  * Results:
  1612.  *    TCL_OK is returned if all went well.  Otherwise, TCL_ERROR is
  1613.  *    returned and interp->result is set to contain an error message.
  1614.  *
  1615.  * Side effects:
  1616.  *    Slave windows get taken over by the gridbag.
  1617.  *
  1618.  *----------------------------------------------------------------------
  1619.  */
  1620.  
  1621. static int
  1622. ConfigureSlaves(interp, tkwin, argc, argv)
  1623.     Tcl_Interp *interp;    /* Interpreter for error reporting. */
  1624.     Tk_Window tkwin;        /* Any window in application containing
  1625.                  * slaves.  Used to look up slave names. */
  1626.     int argc;            /* Numb = 0er of elements in argv. */
  1627.     char *argv[];        /* Argument strings:  contains one or more
  1628.                  * window names followed by any number
  1629.                  * of "option value" pairs.  Caller must
  1630.                  * make sure that there is at least one
  1631.                  * window name. */
  1632. {
  1633.     GridBag *masterPtr, *slavePtr, *prevPtr;
  1634.     Tk_Window other, slave, parent, ancestor;
  1635.     int i, j, numWindows, c, length, tmp, positionGiven;
  1636.     int currentColumn=0, numColumns=1;
  1637.     int gotLayout = 0;
  1638.     int gotWidth = 0;
  1639.     int width;
  1640.  
  1641.     /*
  1642.      * Find out how many windows are specified. (shouldn't use harwired symbols)
  1643.      */
  1644.  
  1645.     for (numWindows = 0; numWindows < argc; numWindows++) {
  1646.     if (argv[numWindows][0] != '.'
  1647.          && strcmp(argv[numWindows],"-")!=0
  1648.          && strcmp(argv[numWindows],"^")!=0
  1649.          && strcmp(argv[numWindows],"x")!=0) {
  1650.         break;
  1651.     }
  1652.     }
  1653.     slave = NULL;
  1654.  
  1655.     /*
  1656.      * Iterate over all of the slave windows, parsing the configuration
  1657.      * options for each slave.  It's a bit wasteful to re-parse the
  1658.      * options for each slave, but things get too messy if we try to
  1659.      * parse the arguments just once at the beginning.  For example,
  1660.      * if a slave already is managed we want to just change a few
  1661.      * existing values without resetting everything.  If there are
  1662.      * multiple windows, the -in option only gets processed for the
  1663.      * first window.
  1664.      */
  1665.  
  1666.     masterPtr = NULL;
  1667.     prevPtr = NULL;
  1668.     positionGiven = 0;
  1669.     for (j = 0; j < numWindows; j++) {
  1670.  
  1671.     /* adjust default widget location for non-widgets */
  1672.     if (*argv[j] != '.') {
  1673.         switch (*argv[j]) {
  1674.         case '^':    /* extend the widget in the previous row 
  1675.                  * Since we don't know who the master is yet,
  1676.                  * handle these in a separate pass at the end
  1677.                  */
  1678.             /* no break */
  1679.         case REL_SKIP:    /* skip over the next column */
  1680.             currentColumn++;
  1681.             break;
  1682.         case REL_HORIZ:    /* increase the span, already dealt with */
  1683.             /* not quite right */
  1684.             if (j>0 && (*argv[j-1] == REL_SKIP || *argv[j-1] == '^')) {
  1685.             Tcl_AppendResult(interp, "Invalid grid combination:",
  1686.                 " \"-\" can't follow \"", argv[j-1], "\"",NULL);
  1687.             return TCL_ERROR;
  1688.             }
  1689.             break;
  1690.         default:
  1691.             panic("Invalid grid position indicator");
  1692.         }
  1693.         continue;
  1694.     }
  1695.  
  1696.     for (numColumns=1; j+numColumns < numWindows && *argv[j+numColumns] == REL_HORIZ;
  1697.         numColumns++) {
  1698.         /* null body */
  1699.     }
  1700.     slave = Tk_NameToWindow(interp, argv[j], tkwin);
  1701.     if (slave == NULL) {
  1702.         return TCL_ERROR;
  1703.     }
  1704.     if (Tk_IsTopLevel(slave)) {
  1705.         Tcl_AppendResult(interp, "can't manage \"", argv[j],
  1706.             "\": it's a top-level window", (char *) NULL);
  1707.         return TCL_ERROR;
  1708.     }
  1709.     slavePtr = GetGridBag(slave);
  1710.  
  1711.     /*
  1712.      * The following statement is taken from tkPack.c:
  1713.      *
  1714.      * "If the slave isn't currently managed, reset all of its
  1715.      * configuration information to default values (there could
  1716.      * be old values left from a previous packer)."
  1717.      *
  1718.      * I disagree with this statement.  If a slave is disabled (using
  1719.      * "forget") and then re-enabled, I submit that 90% of the time the
  1720.      * programmer will want it to retain its old configuration information.
  1721.      * If the programmer doesn't want this behavior, then she can reset the
  1722.      * defaults for herself, but she will never have to worry about keeping
  1723.      * track of the old state. 
  1724.      */
  1725.  
  1726.     for (i = numWindows; i < argc; i+=2) {
  1727.         if ((i+2) > argc) {
  1728.         Tcl_AppendResult(interp, "extra option \"", argv[i],
  1729.             "\" (option with no value?)", (char *) NULL);
  1730.         return TCL_ERROR;
  1731.         }
  1732.         length = strlen(argv[i]);
  1733.         if (length < 2) {
  1734.         goto badOption;
  1735.         }
  1736.         c = argv[i][1];
  1737.         if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
  1738.         if (j == 0) {
  1739.             other = Tk_NameToWindow(interp, argv[i+1], tkwin);
  1740.             if (other == NULL) {
  1741.             return TCL_ERROR;
  1742.             }
  1743.             if (other == slave) {
  1744.             sprintf(interp->result,"Window can't be managed in itself");
  1745.             return TCL_ERROR;
  1746.             }
  1747.             masterPtr = GetGridBag(other);
  1748.             prevPtr = masterPtr->slavePtr;
  1749.             if (prevPtr != NULL) {
  1750.             while (prevPtr->nextPtr != NULL) {
  1751.                 prevPtr = prevPtr->nextPtr;
  1752.             }
  1753.             }
  1754.             positionGiven = 1;
  1755.         }
  1756.         } else if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
  1757.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1758.             || (tmp < 0)) {
  1759.             Tcl_ResetResult(interp);
  1760.             Tcl_AppendResult(interp, "bad ipadx value \"", argv[i+1],
  1761.                 "\": must be positive screen distance",
  1762.                 (char *) NULL);
  1763.             return TCL_ERROR;
  1764.         }
  1765.         slavePtr->iPadX = tmp*2;
  1766.         } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
  1767.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1768.             || (tmp< 0)) {
  1769.             Tcl_ResetResult(interp);
  1770.             Tcl_AppendResult(interp, "bad ipady value \"", argv[i+1],
  1771.                 "\": must be positive screen distance",
  1772.                 (char *) NULL);
  1773.             return TCL_ERROR;
  1774.         }
  1775.         slavePtr->iPadY = tmp*2;
  1776.         } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
  1777.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1778.             || (tmp< 0)) {
  1779.             Tcl_ResetResult(interp);
  1780.             Tcl_AppendResult(interp, "bad padx value \"", argv[i+1],
  1781.                 "\": must be positive screen distance",
  1782.                 (char *) NULL);
  1783.             return TCL_ERROR;
  1784.         }
  1785.         slavePtr->padX = tmp*2;
  1786.         } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
  1787.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1788.             || (tmp< 0)) {
  1789.             Tcl_ResetResult(interp);
  1790.             Tcl_AppendResult(interp, "bad pady value \"", argv[i+1],
  1791.                 "\": must be positive screen distance",
  1792.                 (char *) NULL);
  1793.             return TCL_ERROR;
  1794.         }
  1795.         slavePtr->padY = tmp*2;
  1796.         } else if ((c == 'c') && (strcmp(argv[i], "-column") == 0)) {
  1797.         if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
  1798.             Tcl_ResetResult(interp);
  1799.             Tcl_AppendResult(interp, "bad column value \"", argv[i+1],
  1800.                 "\": must be a non-negative integer", (char *)NULL);
  1801.             return TCL_ERROR;
  1802.         }
  1803.         slavePtr->gridColumn = tmp;
  1804.         } else if ((c == 'r') && (strcmp(argv[i], "-row") == 0)) {
  1805.         if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
  1806.             Tcl_ResetResult(interp);
  1807.             Tcl_AppendResult(interp, "bad grid value \"", argv[i+1],
  1808.                 "\": must be a non-negative integer", (char *)NULL);
  1809.             return TCL_ERROR;
  1810.         }
  1811.         slavePtr->gridRow = tmp;
  1812.         } else if ((c == 'c') && (strcmp(argv[i], "-columnspan") == 0)) {
  1813.         if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp <= 0) {
  1814.             Tcl_ResetResult(interp);
  1815.             Tcl_AppendResult(interp, "bad columnspan value \"", argv[i+1],
  1816.                 "\": must be a positive integer", (char *)NULL);
  1817.             return TCL_ERROR;
  1818.         }
  1819.         slavePtr->gridWidth = tmp;
  1820.         gotWidth++;
  1821.         } else if ((c == 'r') && (strcmp(argv[i], "-rowspan") == 0)) {
  1822.         if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK) {
  1823.             Tcl_ResetResult(interp);
  1824.             Tcl_AppendResult(interp, "bad rowspan value \"", argv[i+1],
  1825.                 "\": must be a positive integer", (char *)NULL);
  1826.             return TCL_ERROR;
  1827.         }
  1828.         slavePtr->gridHeight = tmp;
  1829. /*
  1830.         } else if ((c == 'w') &&
  1831.             (!strcmp(argv[i], "-weightx") || !strcmp(argv[i], "-wx"))) {
  1832.         if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
  1833.             Tcl_ResetResult(interp);
  1834.             Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
  1835.                 "\": must be a double", (char *)NULL);
  1836.             return TCL_ERROR;
  1837.         }
  1838.         slavePtr->weightX = tmp_dbl;
  1839.         }
  1840.         else if ((c == 'w') &&
  1841.             (!strcmp(argv[i], "-weighty") || !strcmp(argv[i], "-wy"))) {
  1842.         if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
  1843.             Tcl_ResetResult(interp);
  1844.             Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
  1845.                 "\": must be a double", (char *)NULL);
  1846.             return TCL_ERROR;
  1847.         }
  1848.         slavePtr->weightY = tmp_dbl;
  1849. */
  1850.         } else if ((c == 's') && strcmp(argv[i], "-sticky") == 0) {
  1851.         int sticky = StringToSticky(argv[i+1]);
  1852.         if (sticky == -1) {
  1853.             Tcl_AppendResult(interp, "bad stickyness value \"", argv[i+1],
  1854.                 "\": must be a string containing n, e, s, and/or w", (char *)NULL);
  1855.             return TCL_ERROR;
  1856.         }
  1857.         slavePtr->flags = sticky | (slavePtr->flags & ~STICK_ALL);
  1858.         } else {
  1859.         badOption:
  1860.         Tcl_AppendResult(interp, "unknown or ambiguous option \"",
  1861.             argv[i], "\": must be -in, -sticky, ",
  1862.             "-row, -column, -rowspan, -columnspan, ",
  1863.             "-ipadx, -ipady, -padx or -pady.",
  1864.             (char *) NULL);
  1865.         return TCL_ERROR;
  1866.         }
  1867.     }
  1868.  
  1869.     /*
  1870.      * If no position in a gridbag list was specified and the slave
  1871.      * is already managed, then leave it in its current location in
  1872.      * its current gridbag list.
  1873.      */
  1874.  
  1875.     if (!positionGiven && (slavePtr->masterPtr != NULL)) {
  1876.         masterPtr = slavePtr->masterPtr;
  1877.         goto scheduleLayout;
  1878.     }
  1879.  
  1880.     /*
  1881.      * If the slave is going to be put back after itself then
  1882.      * skip the whole operation, since it won't work anyway.
  1883.      */
  1884.  
  1885.     if (prevPtr == slavePtr) {
  1886.         masterPtr = slavePtr->masterPtr;
  1887.         goto scheduleLayout;
  1888.     }
  1889.     
  1890.     /*
  1891.      * If the "-in" option has not been specified, arrange for the
  1892.      * slave to go at the end of the order for its parent.
  1893.      */
  1894.     
  1895.     if (!positionGiven) {
  1896.         masterPtr = GetGridBag(Tk_Parent(slave));
  1897.         prevPtr = masterPtr->slavePtr;
  1898.         if (prevPtr != NULL) {
  1899.         while (prevPtr->nextPtr != NULL) {
  1900.             prevPtr = prevPtr->nextPtr;
  1901.         }
  1902.         }
  1903.     }
  1904.  
  1905.     /*
  1906.      * Make sure that the slave's parent is either the master or
  1907.      * an ancestor of the master.
  1908.      */
  1909.     
  1910.     parent = Tk_Parent(slave);
  1911.     for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
  1912.         if (ancestor == parent) {
  1913.         break;
  1914.         }
  1915.         if (Tk_IsTopLevel(ancestor)) {
  1916.         Tcl_AppendResult(interp, "can't put ", argv[j],
  1917.             " inside ", Tk_PathName(masterPtr->tkwin),
  1918.             (char *) NULL);
  1919.         return TCL_ERROR;
  1920.         }
  1921.     }
  1922.  
  1923.     /*
  1924.      * Unlink the slave if it's currently managed, then position it
  1925.      * after prevPtr.
  1926.      */
  1927.  
  1928.     if (slavePtr->masterPtr != NULL) {
  1929.         Unlink(slavePtr);
  1930.     }
  1931.     slavePtr->masterPtr = masterPtr;
  1932.     if (prevPtr == NULL) {
  1933.         slavePtr->nextPtr = masterPtr->slavePtr;
  1934.         masterPtr->slavePtr = slavePtr;
  1935.     } else {
  1936.         slavePtr->nextPtr = prevPtr->nextPtr;
  1937.         prevPtr->nextPtr = slavePtr;
  1938.     }
  1939.     Tk_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
  1940.     prevPtr = slavePtr;
  1941.  
  1942.     /* assign default row and column */
  1943.  
  1944.     if (slavePtr->gridColumn == -1) {
  1945.         slavePtr->gridColumn = currentColumn;
  1946.     }
  1947.     slavePtr->gridWidth += numColumns - 1;
  1948.     if (slavePtr->gridRow == -1) {
  1949.         if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
  1950.         slavePtr->gridRow = masterPtr->layoutCache->lastRow;
  1951.     }
  1952.  
  1953.     /*
  1954.      * Arrange for the parent to be re-arranged at the first
  1955.      * idle moment.
  1956.      */
  1957.  
  1958.     scheduleLayout:
  1959.     if (masterPtr->abortPtr != NULL) {
  1960.         *masterPtr->abortPtr = 1;
  1961.     }
  1962.     masterPtr->valid = 0;
  1963.     if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  1964.         masterPtr->flags |= REQUESTED_RELAYOUT;
  1965.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  1966.     }
  1967.     currentColumn += slavePtr->gridWidth;
  1968.     numColumns = 1;
  1969.     }
  1970.  
  1971.     /* now look for all the "^"'s */
  1972.  
  1973.     for (j = 0; j < numWindows; j++) {
  1974.     struct GridBag *otherPtr;
  1975.         char *lastWindow;    /* use this window to base current row/col on */
  1976.     int match;        /* found a match for the ^ */
  1977.  
  1978.         if (*argv[j] == '.') {
  1979.         lastWindow = argv[j];
  1980.     }
  1981.     if (*argv[j] != '^') {
  1982.         continue;
  1983.     }
  1984.     for (width=1; width+j < numWindows && *argv[j+width] == '^'; width++) {
  1985.         /* Null Body */
  1986.     }
  1987.     other = Tk_NameToWindow(interp, lastWindow, tkwin);
  1988.     otherPtr = GetGridBag(other);
  1989.     if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
  1990.  
  1991.     for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  1992.                      slavePtr = slavePtr->nextPtr) {
  1993.  
  1994.         if (slavePtr->gridWidth == width
  1995.             && slavePtr->gridColumn == otherPtr->gridColumn + otherPtr->gridWidth
  1996.             && slavePtr->gridRow + slavePtr->gridHeight == otherPtr->gridRow) {
  1997.         slavePtr->gridHeight++;
  1998.         match++;
  1999.         }
  2000.         lastWindow = Tk_PathName(slavePtr->tkwin);
  2001.     }
  2002.     if (!match) {
  2003.         Tcl_AppendResult(interp, "can't find slave to extend with \"^\"",
  2004.             " after ",lastWindow,
  2005.             (char *) NULL);
  2006.         return TCL_ERROR;
  2007.     }
  2008.     j += width - 1;
  2009.     }
  2010.     return TCL_OK;
  2011. }
  2012.  
  2013. /* convert "Sticky" bits into a string */
  2014.  
  2015. static void
  2016. StickyToString(flags, result)
  2017.     int flags;        /* the sticky flags */
  2018.     char *result;    /* where to put the result */
  2019. {
  2020.     int count = 0;
  2021.     if (flags&STICK_NORTH) result[count++] = 'n';
  2022.     if (flags&STICK_EAST) result[count++] = 'e';
  2023.     if (flags&STICK_SOUTH) result[count++] = 's';
  2024.     if (flags&STICK_WEST) result[count++] = 'w';
  2025.     if (count) {
  2026.     result[count] = '\0';
  2027.     } else {
  2028.     sprintf(result,"{}");
  2029.     }
  2030. }
  2031.  
  2032. /* convert sticky string to flags */
  2033.  
  2034. static int
  2035. StringToSticky(string)
  2036.     char *string;
  2037. {
  2038.     int sticky = 0;
  2039.     char c;
  2040.  
  2041.     while ((c = *string++) != '\0') {
  2042.     switch (c) {
  2043.         case 'n': case 'N': sticky |= STICK_NORTH; break;
  2044.         case 'e': case 'E': sticky |= STICK_EAST;  break;
  2045.         case 's': case 'S': sticky |= STICK_SOUTH; break;
  2046.         case 'w': case 'W': sticky |= STICK_WEST;  break;
  2047.         case ' ': case ',': case '\t': case '\r': case '\n': break;
  2048.         default: return -1;
  2049.     }
  2050.     }
  2051.     return sticky;
  2052. }        
  2053.